package Server

import (
	"MyTodesk_server/Domain/ServerMsg"
	"fmt"
	"net"

	"golang.org/x/sys/unix"
	"google.golang.org/protobuf/proto"
)

const (
	maxEvent = 100 //最大连接数
	BufSize  = 1024
)

type Server struct {
	listen      net.Listener
	listenFd    int
	epollFd     int
	clients     map[int]*ClientSession
	remoteCodes map[string]int
}

/*
*

	初始化服务器
*/
func (S *Server) Init(add string) error {
	// 初始化clients
	S.clients = make(map[int]*ClientSession)
	// 初始化remoteCodes
	S.remoteCodes = make(map[string]int)
	// 创建服务器套接子
	var err error
	S.listen, err = net.Listen("tcp", add)
	if err != nil {
		fmt.Println("Listrn error:", err)
		return err
	}
	file, err := S.listen.(*net.TCPListener).File()
	if err != nil {
		fmt.Println("File() error:", err)
		return err
	}
	S.listenFd = int(file.Fd())

	//创建epoll实例
	S.epollFd, err = unix.EpollCreate1(0)
	if err != nil {
		fmt.Println("EpollCreate error:", err)
		return err
	}

	//将监听套接子加入到epoll实例中
	event := unix.EpollEvent{
		Events: unix.EPOLLIN,
		Fd:     int32(S.listenFd),
	}
	err = unix.EpollCtl(S.epollFd, unix.EPOLL_CTL_ADD, S.listenFd, &event)
	if err != nil {
		fmt.Println("EpollCtl error:", err)
		return err
	}
	fmt.Println("init successfully!,address:", add)
	return nil
}

/*
运行服务器
*/
func (S *Server) Run() error {
	events := make([]unix.EpollEvent, maxEvent)
	for {
		fmt.Println("wait connect ....")
		// 开启监听
		n, err := unix.EpollWait(S.epollFd, events, -1)
		if err != nil {
			fmt.Println("EpollWait error:", err)
			return err
		}
		// 接受监听到的文件描述
		for i := 0; i < n; i++ {
			fd := int(events[i].Fd)
			//有客户端连接
			if fd == S.listenFd {
				con, err := S.listen.Accept()
				if err != nil {
					fmt.Println("Accept erreo:", err)
					con.Close()
					return err
				}
				// 处理连接
				go S.HandNewConn(con)
			} else { //客户端发来信息
				fmt.Println("client send a message,client address:", S.clients[fd].Conn.RemoteAddr())
				buf := make([]byte, BufSize)
				n, err := unix.Read(fd, buf)
				if err != nil {
					fmt.Println("Read error:", err)
					clientSession := S.clients[fd]
					S.HeadConClose(clientSession)
					return err
				}
				fmt.Println("Client Message:", buf[0:n], "Message size:", n)
				if n == 0 {
					clientSession := S.clients[fd]
					S.HeadConClose(clientSession)
					continue
				}
				go S.HeadClientRequest(fd, buf[0:n], n) //这里buf[0:n]很重要！！！！
			}

		}

	}
}

/*
处理客户端请求
*/
func (this *Server) HeadClientRequest(fd int, buf []byte, bufSize int) error {
	msg := this.Alaysis(buf)
	if msg == nil {
		//Todo:无法解析的问题
		return nil
	}
	//获取指令的命令
	switch msg.Cmd {
	case 1: //1:请求开启远程
		this.HeadRemote(fd, msg)
		break
	case 2:
		break
	case 3: //3:请求登陆
		this.HeadLogin(fd, msg)
		break
	case 4: //请求关闭远程
		this.HeadCloseRemote(fd, msg)
		break
	default:

	}

	return nil
}

/*
处理关闭远程请求
*/
func (this *Server) HeadCloseRemote(thizfd int, msg *ServerMsg.MainMsg) {
	//1.找到被控制端的fd
	clientSession := this.clients[thizfd]
	fd := clientSession.otherfd
	//2. 发送消息
	unix.Write(fd, this.Serialize(msg))
	//3. 更改控制端session状态
	clientSession.status = 0
	clientSession.otherfd = -1
	//4. 更改被控端session状态
	clientSession = this.clients[fd]
	clientSession.status = 0
	clientSession.otherfd = -1
}

/*
处理客户端的断开请求
*/
func (this *Server) HeadConClose(client *ClientSession) {
	fmt.Println("client close,client address:", client.Conn.RemoteAddr())
	//从epoll监听符集合中移除
	unix.EpollCtl(this.epollFd, unix.EPOLL_CTL_DEL, client.fd, nil)
	//关闭套接字
	unix.Close(client.fd)
	//从clients中清除
	delete(this.clients, client.fd)
	//从remoteCodes中删除（如果有）
	_, isexit := this.remoteCodes[client.remoteCode]
	if isexit {
		delete(this.remoteCodes, client.remoteCode)
	}
}

/*
处理客户端的连接请求
*/
func (S *Server) HandNewConn(con net.Conn) error {
	fmt.Println("new client")
	//打印客户端信息
	clientAdd := con.RemoteAddr()
	fmt.Println("client add:", clientAdd)

	file, err := con.(*net.TCPConn).File()
	if err != nil {
		fmt.Print("file error:", err)
		return err
	}
	//处理连接的客户端
	//1 加入到epoll事件
	event := unix.EpollEvent{
		Events: unix.EPOLLIN,
		Fd:     int32(file.Fd()),
	}
	err2 := unix.EpollCtl(S.epollFd, unix.EPOLL_CTL_ADD, int(file.Fd()), &event)
	if err2 != nil {
		fmt.Println("EpollCtl error:", err2)
	}

	//2 加入到clients
	clientSession := &ClientSession{
		fd:         int(file.Fd()),
		Conn:       con,
		remoteCode: con.RemoteAddr().String(), //todo:这里应该通过数据库查询获取远程控制代码,现以他的ip地址+端口代替
	}
	S.clients[int(file.Fd())] = clientSession

	return nil
}

/*
关闭服务器
*/
func (this *Server) Stop() {
	this.listen.Close()
	unix.Close(this.epollFd)
	for key, _ := range this.clients {
		unix.Close(key)
	}
}

/*
将protobuf获得的byte串转为Message类
*/
func (this *Server) Alaysis(buf []byte) *ServerMsg.MainMsg {
	msg := &ServerMsg.MainMsg{}
	err1 := proto.Unmarshal(buf, msg)
	if err1 != nil {
		fmt.Println("error:Unmarshal error", err1)
		return nil
	}
	fmt.Println("cmd:", msg.Cmd)
	return msg
}

/*
	序列化：将Message类转为byte串
*/

func (this *Server) Serialize(m *ServerMsg.MainMsg) []byte {
	ret, err := proto.Marshal(m)
	if err != nil {
		fmt.Println("error : Marshal error:", err)
		return nil
	}
	return ret
}

/*
处理客户端请求开启远程
*/
func (this *Server) HeadRemote(thizfd int, msg *ServerMsg.MainMsg) error {
	fmt.Println("请求开启远程...")
	//1 获取控制端传来的远程控制码，以及他自己的ip和UDP端口
	remoteMsg := &ServerMsg.RemoteCtlMsg{}
	err1 := proto.Unmarshal([]byte(msg.Payload), remoteMsg)
	if err1 != nil {
		fmt.Println("error:Unmarshal remoteMsg error:", err1)
		return err1
	}
	fmt.Println("Codes:", remoteMsg.Codes)
	ip := net.IPv4(byte(remoteMsg.Ipv4), byte(remoteMsg.Ipv4>>8), byte(remoteMsg.Ipv4>>16), byte(remoteMsg.Ipv4>>24))
	fmt.Println("Ipv4:", ip.String())
	fmt.Println("Port:", remoteMsg.Port)

	//2 检测被控端是否在线
	fd, exist := this.remoteCodes[string(remoteMsg.Codes)]
	if !exist {
		fmt.Println("rempte desktop is not online")
		//todo: 不在线处理
		//4 通知控制端
		_, err := unix.Write(thizfd,
			this.Serialize(
				&ServerMsg.MainMsg{
					Cmd:     1,
					Payload: []byte{'0'},
				},
			),
		)
		if err != nil {
			fmt.Println("error:Write error:", err)
			return err
		}
		return nil
	}
	//3 通知被控端开启远程
	_, err := unix.Write(fd,
		this.Serialize(
			&ServerMsg.MainMsg{
				Cmd:           2,
				PayloadLength: 14,
				Payload:       msg.Payload,
			},
		),
	)
	if err != nil {
		fmt.Println("error:Write error:", err)
		return err
	}

	//4 通知控制端
	unix.Write(thizfd,
		this.Serialize(
			&ServerMsg.MainMsg{
				Cmd:     1,
				Payload: []byte{'1'},
			},
		),
	)

	//5 向控制端的session加入消息
	clientSession := this.clients[thizfd]
	clientSession.status = 2
	clientSession.otherfd = fd

	// 向被控端的session加入消息
	clientSession = this.clients[fd]
	clientSession.status = 1
	clientSession.otherfd = thizfd

	fmt.Println("note remote target!,target fd:", fd, "  thiz fd:", thizfd)
	return nil
}

/*
处理用户登陆请求
*/
func (this *Server) HeadLogin(fd int, msg *ServerMsg.MainMsg) error {

	//1 解析用户登陆信息
	loginMsg := &ServerMsg.LoginMsg{}
	err1 := proto.Unmarshal([]byte(msg.Payload), loginMsg)
	if err1 != nil {
		fmt.Println("error:Unmarshal loginMsg error:", err1)
		return err1
	}

	//2 TODO:判断登陆是否成功逻辑
	loginMsg.Code = loginMsg.Pwd
	binMsg, _ := proto.Marshal(loginMsg)

	//3 加入到remotecodes
	this.remoteCodes[loginMsg.Code] = int(fd)
	ClientSession := this.clients[fd]
	ClientSession.remoteCode = loginMsg.Code
	this.clients[fd] = ClientSession

	//3 反馈
	mainMsg := &ServerMsg.MainMsg{
		Cmd:     3,
		Payload: binMsg,
	}
	data, err := proto.Marshal(mainMsg)
	if err != nil {
		fmt.Println("err:proto.Marshal error", err)
	}
	_, err2 := unix.Write(fd, data)

	if err2 != nil {
		fmt.Println("err:Write error", err2)
	}
	return nil
}
